/// ===========================================================================
/// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
/// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
/// PURPOSE.
/// ===========================================================================
/// 
/// Project:        MOSS Faceted Search
/// Author:         Leonid Lyublinski (leonidly@microsoft.com)
/// Company:        Microsoft Services
/// Date:           05/04/2007  Version:        1.0
///
/// ===========================================================================

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml.Serialization;
using System.Data;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.Office.Server;
using Microsoft.Office.Server.Search.Query;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace Microsoft.SharePoint.Portal.ExtendedSearch.WebControls
{
    /// <summary>
    /// Custom search bread crumbs web part.
    /// The web part bound to the querystring in and out.
    /// </summary>
    [System.Web.AspNetHostingPermission(System.Security.Permissions.SecurityAction.Demand, Level = System.Web.AspNetHostingPermissionLevel.Minimal)]
    [System.Web.AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = System.Web.AspNetHostingPermissionLevel.Minimal)]
    [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, Execution = true)]
    //[Microsoft.SharePoint.Security.SharePointPermission(System.Security.Permissions.SecurityAction.Demand, ObjectModel = true)]
    //[System.Security.Permissions.RegistryPermission(System.Security.Permissions.SecurityAction.Demand, Unrestricted = true)]
    //[Microsoft.SharePoint.Security.WebPartPermission(System.Security.Permissions.SecurityAction.Demand, Connections = true)]
    //[ToolboxData("<{0}:SearchBreadCrumbs runat=server></{0}:SearchBreadCrumbs>")]
    [XmlRoot(Namespace = "Microsoft.SharePoint.Portal.ExtendedSearch")]
    public class SearchBreadCrumbs : System.Web.UI.WebControls.WebParts.WebPart
    {
        #region Constants
        
        #endregion

        #region Fields
        private string _scope;
        private string _k;
        private int _start;
        private string _v;
        private NameValueCollection _properties;

        private string _headerCss; // = "ms-bold";
        private string _facetCss; // = "ms-bold";
        private string _separator; // = " + ";
        private string _facetContainerCss; // = "ms-quicklaunch";
        private string _containerCss; // = "ms-quicklaunch";
        private int _repeatColumns;

        private string _crumbCss; // = "ms-consoleqaback";
        private string _crumbHoverCss; // = "ms-consoleqabackhover";
        private bool _connected;
        private DataTable _crumbTable;
        private XmlNode _xmlColumns;
        #endregion

        #region Properties
        /// <summary>
        /// The CSS class for the facet.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Crumb CSS")]
        [Description("CSS class for the crumb.")]
        public string CrumbCss
        {
            get { return _crumbCss; }
            set { _crumbCss = value; }
        }
        /// <summary>
        /// The CSS class for the facet.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Crumb Hover CSS")]
        [Description("CSS class for the crumb hover.")]
        public string CrumbHoverCss
        {
            get { return _crumbHoverCss; }
            set { _crumbHoverCss = value; }
        }     
        /// <summary>
        /// The CSS class for the face datalist.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Repeat Columns")]
        [Description("Number of table columns.")]
        public int RepeatColumns
        {
            get
            {
                return _repeatColumns;
            }
            set
            {
                _repeatColumns = value;
            }
        }

        /// <summary>
        /// The CSS class for the face datalist.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Container CSS Class")]
        [Description("Select CSS class for the entire container.")]
        public string ContainerCss
        {
            get
            {
                return _containerCss;
            }
            set
            {
                _containerCss = value;
            }
        }

        /// <summary>
        /// The CSS class for the face value.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Keyword CSS Class")]
        [Description("Select CSS class for the keyword.")]
        public string HeaderCss
        {
            get
            {
                return _headerCss;
            }
            set
            {
                _headerCss = value;
            }
        }
 
        /// <summary>
        /// The CSS class for the face datalist.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Facet Container CSS Class")]
        [Description("Select CSS class for the facet container.")]
        public string FacetContainerCss
        {
            get
            {
                return _facetContainerCss;
            }
            set
            {
                _facetContainerCss = value;
            }
        }

        /// <summary>
        /// The CSS class for the face name.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Facet CSS Class")]
        [Description("Select CSS class for the facet header.")]
        public string FacetCss
        {
            get
            {
                return _facetCss;
            }
            set
            {
                _facetCss = value;
            }
        }

     
        /// <summary>
        /// A text used as a separator between bread crumbs.
        /// </summary>
        [WebBrowsable(true)]
        [Category("Results Display")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Personalizable(PersonalizationScope.Shared)]
        [FriendlyName("Bread Separator")]
        [Description("Will be used to separate bread crumbs.")]
        public string Separator
        {
            get
            {
                return _separator;
            }
            set
            {
                _separator = value;
            }
        }


        #endregion

        #region Delegates
        /// <summary>
        /// Constructs the Controls collection of the webpart. 
        /// </summary _
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            try
            {
                if (Context.Request.QueryString != null)
                {
                    SearchQuery query = Utility.ExtractQuery();
                    _k = query.Keywords;
                    _scope = query.Scope;
                    _start = query.Start;
                    _v = query.View;
                    _properties = query.Properties;
                    if (Parent != null && Parent.GetType().FullName == "Microsoft.SharePoint.WebPartPages.WebPartPreview")
                    {
                        // fake something for WP preview only
                        _k = "discount";
                        _properties.Add("Category", "Electronics");
                        _properties.Add("Manufacturer", "Sony");
                        _properties.Add("In stock", "Yes");
                    }
           
                    
                    InitCrumbTable();
                    foreach (string prop in _properties)
                    {
                        DataRow row = _crumbTable.NewRow();
                        row["FacetName"] = prop;
                        row["FacetValue"] = _properties[prop];
                        if (_connected)
                        {
                            row["FacetDisplayName"] = GetFacetDisplayName(prop);
                            row["FacetDisplayValue"] = GetFacetDisplayValue(prop, _properties[prop]);
                        }
                        _crumbTable.Rows.Add(row);
                    }

                    Label lSearch = new Label();
                    lSearch.EnableViewState = false;
                    lSearch.CssClass = _headerCss;
                    lSearch.Text = (string.IsNullOrEmpty(_k))?"":Common.Constants.SEARCH_BREADCRUMBS_WEBPART_PREFIX + _k;
                                                        
                    DataList list = new DataList();
                    list.CssClass = _facetContainerCss;
                    list.CellPadding = 2;
                    list.CellSpacing = 0;
                    list.RepeatDirection = RepeatDirection.Horizontal;
                    list.RepeatColumns = _repeatColumns;
                    list.ItemTemplate = new CrumbTemplate(ListItemType.Item, _facetCss, _crumbCss, _crumbHoverCss);
                    if (!string.IsNullOrEmpty(_separator)) list.SeparatorTemplate = new SeparatorTemplate(ListItemType.Separator, _facetCss, _separator);
                    list.DataSource = _crumbTable;                
                    list.DataBind();

                    HtmlTable table = BreadCrumbsTable(lSearch, list);

                    Controls.Add(table);
                }
            }
            catch (Exception ex)
            {
                // remove label when done troubleshooting
                Label error = new Label();
                error.CssClass = "ms-error";
                error.Text = ex.Message;
                Controls.Add(error);
            }
        }
        #endregion

        #region Private Methods
        private HtmlTable BreadCrumbsTable(Control keyword, Control metas)
        {
            HtmlTable table = new HtmlTable();
            table.Attributes.Add("class", _containerCss);
            table.CellSpacing = 0;
            table.Border = 0;

            HtmlTableRow rowKeyword = new HtmlTableRow();            
            HtmlTableCell cellKeyword = new HtmlTableCell();
            cellKeyword.Controls.Add(keyword);
            cellKeyword.Attributes.Add("class", "ms-sctabrx");
            cellKeyword.NoWrap = true;
            rowKeyword.Cells.Add(cellKeyword);

            if (metas.HasControls())
            {
                HtmlImage img = new HtmlImage();
                img.Src = "/_layouts/images/filteroff.gif";
                img.Border = 0;
                
                HyperLink resetSearch = new HyperLink();
                resetSearch.NavigateUrl = CleanSearch();
                resetSearch.Text = Common.Constants.SEARCH_BREADCRUMBS_WEBPART_RESET_ALL_BUTTON;
                if (!string.IsNullOrEmpty(_k)) cellKeyword.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));
                cellKeyword.Controls.Add(resetSearch);
               
                HyperLink resetFacets = new HyperLink();
                resetFacets.NavigateUrl = CleanUrl();
                resetFacets.Text = Common.Constants.SEARCH_BREADCRUMBS_WEBPART_RESET_BUTTON;
                cellKeyword.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));
                cellKeyword.Controls.Add(resetFacets);
            }
            if (!string.IsNullOrEmpty(_k))
            {
                HyperLink resetKeyword = new HyperLink();
                resetKeyword.NavigateUrl = CleanKeyword();
                resetKeyword.Text = Common.Constants.SEARCH_BREADCRUMBS_WEBPART_RESET_KEYWORD_BUTTON;
                cellKeyword.Controls.Add(new LiteralControl("&nbsp;|&nbsp;"));
                cellKeyword.Controls.Add(resetKeyword);
            }

            table.Rows.Add(rowKeyword);
//
            if (metas.HasControls())
            {
                HtmlTableRow rowMetas = new HtmlTableRow();
                HtmlTableCell cellMetas = new HtmlTableCell();
                cellMetas.NoWrap = true;
                cellMetas.Width = "100%";
                cellMetas.Align = "left";
                cellMetas.Controls.Add(metas);
                rowMetas.Cells.Add(cellMetas);
                table.Rows.Add(rowMetas);
            }

            return table;
        }        

        private string CleanUrl()
        {
            SearchQuery query = Utility.ExtractQuery();
            query.Properties.Clear();
            string qs = Utility.BuildQueryString(query);
            string qsEncoded = string.Format("{0}?{1}", HttpContext.Current.Request.Url.AbsolutePath, qs);
            return qsEncoded;
        }

        private string CleanKeyword()
        {
            SearchQuery query = Utility.ExtractQuery();
            query.Keywords = string.Empty;
            string qs = Utility.BuildQueryString(query);
            string qsEncoded = string.Format("{0}?{1}", HttpContext.Current.Request.Url.AbsolutePath, qs);
            return qsEncoded;
        }

        private string CleanSearch()
        {
            SearchQuery query = Utility.ExtractQuery();
            query.Properties.Clear();
            query.Keywords = "";
            string qs = Utility.BuildQueryString(query);
            string qsEncoded = string.Format("{0}?{1}", HttpContext.Current.Request.Url.AbsolutePath, qs);
            return qsEncoded;
        }

        private void InitCrumbTable()
        {
            _crumbTable = new DataTable("CrumbTable");
            _crumbTable.Columns.Add("FacetName", typeof(string));
            _crumbTable.Columns.Add("FacetDisplayName", typeof(string));
            _crumbTable.Columns.Add("FacetValue", typeof(string));
            _crumbTable.Columns.Add("FacetDisplayValue", typeof(string));
        }

        private string GetFacetDisplayName(string facetName)
        {
            try
            {
                string xpath = string.Format("//Column[@Name='{0}']/@DisplayName", facetName);
                XmlNode node = _xmlColumns.SelectSingleNode(xpath);

                if (node == null) return null;
                else return node.Value;
            }
            catch (Exception ex)
            {
                return null;
            }
            return facetName;
        }
        private string GetFacetDisplayValue(string facetName, string facetValue)
        {
            try
            {
                string xpath = string.Format("//Column[@Name='{0}']/Mappings/Mapping[(contains(@Match, '%') and starts-with('{1}', substring-before(@Match, '%'))) or (@Match = '{1}')]/@DisplayName", facetName, facetValue);
                XmlNode node = _xmlColumns.SelectSingleNode(xpath);

                if (node == null) return null;
                else return node.Value;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        #endregion

        [ConnectionConsumer("Facet Settings", "FacetSettings", AllowsMultipleConnections = false)]
        public void GetFacetSettings(XmlNode facets)
        {
            _xmlColumns = facets;
            _connected = (facets != null);            
        }
    }
}
